﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;

namespace Poet
{
    public static class AppInstanceManager
    {
        public static ReciveArguments OnReciveArguments;
        public static IEnumerable<string> SturtupArgs { get; set; }
        public delegate void ReciveArguments(IEnumerable<string> args);

        private static readonly string EventName;
        private static readonly string MutexName;
        private static readonly string FileName;
        private static bool _enableArgumentRecive;
        private static EventWaitHandle _eventWaitHandle;

        static AppInstanceManager()
        {
            var appName = Application.Current.GetType().Assembly.ManifestModule.ScopeName;
            var windowsIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
            var keyUserName = windowsIdentity != null ? windowsIdentity.User.ToString() : String.Empty;
            // Be careful! Max 260 chars!
            EventName = string.Format(@"{0}{1}-event", appName, keyUserName);
            MutexName = string.Format(@"{0}{1}-mutex", appName, keyUserName);
            FileName = string.Format(@"{0}{1}-file", appName, keyUserName);
        }

        public static void SendCommandLineArgs(IEnumerable<string> args)
        {
Retry:
            try
            {
                using (_eventWaitHandle = EventWaitHandle.OpenExisting(EventName))
                {
                    try
                    {
                        var mutex = Mutex.OpenExisting(MutexName);
                        mutex.WaitOne();
                        using (var sw = new StreamWriter(File.Open(FileName, FileMode.OpenOrCreate)))
                        {
                            foreach (var arg in args)
                            {
                                sw.WriteLine(arg);
                            }
                        }
                        _eventWaitHandle.Set();
                        mutex.ReleaseMutex();
                    }
                    catch (IOException ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }
                Environment.Exit(0);
            }
            catch
            {
                SturtupArgs = args;
                try
                {
                    bool isFirstInstance;
                    _eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, EventName, out isFirstInstance);
                    // new EventWaitHandle was called but EventWaitHandle.OpenExisting no open it. Retry
                    if(!isFirstInstance)
                    {
                        _eventWaitHandle.Close();
                        //_eventWaitHandle.Dispose();
                        goto Retry;
                    }
                    _enableArgumentRecive = true; 
                    // Use RunArgumentReciver() method for quickly application startup
                    //new Thread(ArgumentReciver) { IsBackground = true }.Start();
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.Message);
                }
            }
        }

        public static void RunArgumentReciver()
        {
            if (_enableArgumentRecive) new Thread(ArgumentReciver) { IsBackground = true }.Start();
        }

        public static void ArgumentReciver()
        {
            try
            {
                StreamReader reader = null;
                var args = new List<string>();
                var mutex = new Mutex(false, MutexName);

                while (true)
                {
                    _eventWaitHandle.WaitOne();

                    if (reader == null)
                    {
                        mutex.WaitOne();
                        args.Clear();
                        reader = new StreamReader(File.Open(FileName, FileMode.OpenOrCreate));
                    }
                    var arg = reader.ReadLine();
                    if (arg == null)
                    {
                        reader.Close();
                        reader = null;
                        File.Delete(FileName);
                        _eventWaitHandle.Reset();
                        mutex.ReleaseMutex();
                        if (OnReciveArguments != null)
                            OnReciveArguments(args);
                    }
                    else args.Add(arg);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        public static void Dispose()
        {
            //_eventWaitHandle.Dispose();
        }

        public static bool IsFirstInstance()
        {
            var currentProcess = Process.GetCurrentProcess();
            var remoteProcesses = Process.GetProcessesByName(currentProcess.ProcessName);

            return remoteProcesses.All(remoteProcess =>
                                       remoteProcess.StartInfo.FileName !=
                                       currentProcess.StartInfo.FileName);
        }
    }
}
